{%-set className = resolver.cpp_resolve_namespace(ns)+Name%}
{%-set std = resolver.cpp_resolve_namespace(['std']) %}
{%-set enumType = className+"::Value"%}
{%-set exception %}{{resolver.cpp_get_lib_ns() | join('::')}}::JsonSchemaException{%endset%}
{{className}}::{{Name}}({{enumType}} value)
{
    Set(value);
}

{%-if schema.default is defined %}
{%set emptyConstructor = true %}
{{className}}::{{Name}}()
{
    Set(DEFAULT_VALUE);
}
{%-endif%}

{{className}}::operator {{enumType}}() const
{
    return Get();
}

{{className}}& {{className}}::operator=({{enumType}} value)
{
    Set(value);
    return *this;
}
{%for origNs in originalNamespace %}
namespace {{origNs}} {
{%-endfor%}  
{{std}}ostream& operator<<({{std}}ostream& os, const {{className}}& v)
{
    os << v.EnumToString(v._value);
    return os;
}

bool operator< (const {{className}}& left, const {{className}}& right)
{
    return left._value < right._value;
}

{{std}}size_t hash_value(const {{className}}& e)
{
    return boost::hash_value(e.ToString());
}
{%for origNs in originalNamespace %}}{%-endfor%} // end namespaces

void {{className}}::Set({{enumType}} value)
{
    _value = value;
}

{{enumType}} {{className}}::Get() const
{
    return _value;
}

{{std}}string {{className}}::EnumToString({{enumType}} value)
{
    switch (value)
    {
        {%-for enum in schema.enum%}
        case {{enumType}}::{{enum | enumify}}:
            return "{{enum}}";
        {%-endfor%}
    }
    // By not including a 'defaut' case in the switch statement, we should ensure that
    // all values of the enum are handled.  However, some compilers like to see a return
    // statement when no cases are matched.  While this never happens, throwing here
    // avoids a compiler warning.
    throw std::out_of_range("No valid string for invalid enum value");
}

{{enumType}} {{className}}::StringToEnum(const std::string& input)
{
    {%-for enum in schema.enum %}
    {%if not loop.first%}else {%endif%}if (input == "{{enum}}")
    {
        return {{enumType}}::{{enum | enumify}};
    }
    {%-endfor%}
    else
    {
        throw std::out_of_range("Could not find enum value for string");
    }
}

{{className}} {{className}}::FromJson(const {{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& json)
{
    if (!(json.IsString()))
    {
        throw {{exception}}("JSON wasn't a string");
    }

    {{std}}string testValue = json.GetString();
    try
    {
        return {{className}}(StringToEnum(testValue));
    }
    catch (const std::exception& e)
    {
        throw {{exception}}(e);
    }
}

{{className}} {{className}}::FromString(const {{std}}string& str)
{
    return {{className}}(StringToEnum(str));
}

void {{className}}::ToJson({{resolver.cpp_resolve_namespace(['rapidjson'])}}Value& value, {{resolver.cpp_resolve_namespace(['rapidjson', 'Value'])}}AllocatorType& allocator) const
{
    {{std}}string strValue = EnumToString(_value);
    value.SetString(strValue.c_str(), strValue.size(), allocator);
}

{{std}}string {{className}}::ToString() const
{
    return EnumToString(_value);
}

void {{className}}::SetHandle(const std::string& handle)
{
    _handle = handle;
}

std::string {{className}}::GetHandle() const
{
    return _handle;
}